namespace CLELCore.Assembly.CLELInstructions
{
	public static class BranchUtilities
	{
		public static bool IsBranch(CLELInstruction inst)
		{
			if(inst == null)
				return false;
			return IsBranch(inst.Opcode);
		}

		public static bool IsBranch(byte opcode)
		{
			if(IsShortBranch(opcode) || IsLongBranch(opcode))
				return true;
			return false;
		}
		
		public static bool IsShortBranch(CLELInstruction inst)
		{
			if(inst == null)
				return false;
			return IsShortBranch(inst.Opcode);
		}

		public static bool IsShortBranch(byte opcode)
		{
			if(opcode == CLELOpcode.BRS || opcode == CLELOpcode.BRFALSES 
			|| opcode == CLELOpcode.BRTRUES || opcode == CLELOpcode.BEQS
			|| opcode == CLELOpcode.BGES || opcode == CLELOpcode.BGTS
			|| opcode == CLELOpcode.BLES || opcode == CLELOpcode.BLTS
			|| opcode == CLELOpcode.BNEUNS || opcode == CLELOpcode.BGEUNS
			|| opcode == CLELOpcode.BGTUNS || opcode == CLELOpcode.BLEUNS
			|| opcode == CLELOpcode.BLTUNS || opcode == CLELOpcode.LEAVES)
				return true;
			return false;
		}
		
		public static bool IsLongBranch(CLELInstruction inst)
		{
			if(inst == null)
				return false;
			return IsLongBranch(inst.Opcode);
		}

		public static bool IsLongBranch(byte opcode)
		{
			if(opcode == CLELOpcode.BR || opcode == CLELOpcode.BRFALSE 
			|| opcode == CLELOpcode.BRTRUE || opcode == CLELOpcode.BEQ 
			|| opcode == CLELOpcode.BGE || opcode == CLELOpcode.BGT 
			|| opcode == CLELOpcode.BLE || opcode == CLELOpcode.BLT 
			|| opcode == CLELOpcode.BNEUN || opcode == CLELOpcode.BGEUN 
			|| opcode == CLELOpcode.BGTUN || opcode == CLELOpcode.BLEUN 
			|| opcode == CLELOpcode.BLTUN || opcode == CLELOpcode.LEAVE)
				return true;
			return false;
		}

		public static bool GetShortBranchOffset(CLELInstruction inst,ref sbyte offset)
		{
			if(inst == null)
				return false;
			byte opcode = inst.Opcode;
			if(opcode == CLELOpcode.BRS)
			{
				Brs brs = (Brs)inst;
				offset = brs.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BRFALSES)
			{
				Brfalses brfalses = (Brfalses)inst;
				offset = brfalses.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BRTRUES)
			{
				Brtrues brtrues = (Brtrues)inst;
				offset = brtrues.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BEQS)
			{
				Beqs beqs = (Beqs)inst;
				offset = beqs.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BGES)
			{
				Bges bges = (Bges)inst;
				offset = bges.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BGTS)
			{
				Bgts bgts = (Bgts)inst;
				offset = bgts.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BLES)
			{
				Bles bles = (Bles)inst;
				offset = bles.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BLTS)
			{
				Blts blts = (Blts)inst;
				offset = blts.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BNEUNS)
			{
				Bneuns bneuns = (Bneuns)inst;
				offset = bneuns.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BGEUNS)
			{
				Bgeuns bgeuns = (Bgeuns)inst;
				offset = bgeuns.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BGTUNS)
			{
				Bgtuns bgtuns = (Bgtuns)inst;
				offset = bgtuns.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BLEUNS)
			{
				Bleuns bleuns = (Bleuns)inst;
				offset = bleuns.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BLTUNS)
			{
				Bltuns bltuns = (Bltuns)inst;
				offset = bltuns.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.LEAVES)
			{
				Leaves leaves = (Leaves)inst;
				offset = leaves.Offset;
				return true;
			}
			return false;
		}

		public static bool GetLongBranchOffset(CLELInstruction inst,ref int offset)
		{
			if(inst == null)
				return false;
			byte opcode = inst.Opcode;
			if(opcode == CLELOpcode.BR)
			{
				Br br = (Br)inst;
				offset = br.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BRFALSE)
			{
				Brfalse brfalse = (Brfalse)inst;
				offset = brfalse.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BRTRUE)
			{
				Brtrue brtrue = (Brtrue)inst;
				offset = brtrue.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BEQ)
			{
				Beq beq = (Beq)inst;
				offset = beq.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BGE)
			{
				Bge bge = (Bge)inst;
				offset = bge.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BGT)
			{
				Bgt bgt = (Bgt)inst;
				offset = bgt.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BLE)
			{
				Ble ble = (Ble)inst;
				offset = ble.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BLT)
			{
				Blt blt = (Blt)inst;
				offset = blt.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BNEUN)
			{
				Bneun bneun = (Bneun)inst;
				offset = bneun.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BGEUN)
			{
				Bgeun bgeun = (Bgeun)inst;
				offset = bgeun.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BGTUN)
			{
				Bgtun bgtun = (Bgtun)inst;
				offset = bgtun.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BLEUN)
			{
				Bleun bleun = (Bleun)inst;
				offset = bleun.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.BLTUN)
			{
				Bltun bltun = (Bltun)inst;
				offset = bltun.Offset;
				return true;
			}
			else if(opcode == CLELOpcode.LEAVE)
			{
				Leave leave = (Leave)inst;
				offset = leave.Offset;
				return true;
			}
			return false;
		}

		public static bool GetBranchOffset(CLELInstruction inst,ref int offset)
		{
			int ioff = 0;
			sbyte soff = 0;
			if(GetShortBranchOffset(inst,ref soff))
			{
				offset = (int)soff;
				return true;
			}
			else if(GetLongBranchOffset(inst,ref ioff))
			{
				offset = ioff;
				return true;
			}
			return false;
		}

		public static bool IsUnconditionalBranch(CLELInstruction inst)
		{
			if(inst == null)
				return false;
			byte opcode = inst.Opcode;
			if(opcode == CLELOpcode.BR)
				return true;
			else if(opcode == CLELOpcode.LEAVE)
				return true;
			else if(opcode == CLELOpcode.BRS)
				return true;
			else if(opcode == CLELOpcode.LEAVES)
				return true;
			return false;
		}

		public static bool IsConditionalBranch(CLELInstruction inst)
		{
			return !BranchUtilities.IsUnconditionalBranch(inst) && inst != null;
		}
	}
}
